/** @file bt_hci.cxx
  *  
  * @brief This file contains the exported interface functions to act as
  * Windows HCI Transport driver.
  * 
  * (c) Copyright 2003-2007, Marvell International Ltd.
  * All Rights Reserved
  */

#include <windows.h>
#include <types.h>
#include <bt_hcip.h>
#include <bt_os.h>

#include "bt_def.h"
#include "bt_dev.h"
#include "bt_func.h"
#include "bt_debug.h"

#include "igxBug.h"


#ifdef DEBUG
// Need this for sdcard modules
DBGPARAM dpCurSettings = {
    _T("BTHSDIO"), 
    {
        _T(""), _T(""), _T(""), _T(""),
        _T(""), _T(""), _T(""), _T(""),
        _T(""), _T(""), _T(""), _T(""),
        _T(""), _T(""), _T(""), _T("") 
    },
    0x0 
};
#endif


/********************************************************
        Local Variables
********************************************************/
static HCI_TransportCallback pFnHCICallback = NULL;

/********************************************************
        Global Variables
********************************************************/

/********************************************************
        Local Functions
********************************************************/

/********************************************************
        Global Functions
********************************************************/

/*
 * This function is called from HCI to read transport-specific paramters.
 */
int HCI_ReadHciParameters(HCI_PARAMETERS *pParms)
{
    HKEY hk;
    int fRetVal = TRUE;


    DBGPRINT(DBG_HCI, (L"HCI_ReadHciParameters\n\r"));

    if (pParms->uiSize < sizeof(*pParms))
    {
        DBGPRINT(DBG_HCI, (L"Buffer is too small !\n\r"));
        fRetVal = FALSE;
        goto exit;
    }

    memset(pParms, 0, sizeof(*pParms));

    pParms->uiSize                  = sizeof(*pParms);
    pParms->fInterfaceVersion       = HCI_INTERFACE_VERSION_1_1;
    pParms->iMaxSizeRead            = MRVL_BT_PACKET_SIZE_W;
    pParms->iMaxSizeWrite           = MRVL_BT_PACKET_SIZE_R;
    pParms->iWriteBufferHeader      = MRVL_BT_BUFFER_HEADER_SIZE;
    pParms->iReadBufferHeader       = MRVL_BT_BUFFER_HEADER_SIZE;
    pParms->fHardwareVersion        = HCI_HARDWARE_VERSION_V_1_0_B;
    pParms->uiWriteTimeout          = HCI_DEFAULT_WRITE_TIMEOUT;
    pParms->uiDriftFactor           = HCI_DEFAULT_DRIFT;
    pParms->uiFlags                 = 0;
    pParms->iScoWriteLowNumPackets  = MRVL_BT_SCO_DEFAULT_WRITE_LOW_NUM_PACKETS;
    pParms->iScoWriteNumPackets     = MRVL_BT_SCO_DEFAULT_WRITE_HIGH_NUM_PACKETS;
    pParms->iScoWritePacketSize     = MRVL_BT_SCO_DEFAULT_PACKET_SIZE;
    pParms->iScoSampleSize          = MRVL_BT_SCO_DEFAULT_SAMPLE_SIZE;

    if (RegOpenKeyEx(HKEY_BASE, L"Software\\Microsoft\\Bluetooth\\hci", 0, KEY_READ, &hk)
        == ERROR_SUCCESS)
    {
        DWORD dwType = 0;

        DWORD dwData = 0;
        DWORD dwSize = sizeof(dwData);
        if ((RegQueryValueEx(hk, L"ResetDelay", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->uiResetDelay = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx(hk, L"SpecV10a", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->fHardwareVersion = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx(hk, L"SpecV11", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->fHardwareVersion = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx(hk, L"WriteTimeout", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->uiWriteTimeout = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx(hk, L"Flags", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->uiFlags = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx(hk, L"Drift", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->uiDriftFactor = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx (hk, L"ScoWriteLowNumPackets", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->iScoWriteLowNumPackets = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx (hk, L"ScoWriteNumPackets", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->iScoWriteNumPackets = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx (hk, L"ScoWritePacketSize", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->iScoWritePacketSize = dwData;

        dwData = 0;
        dwSize = sizeof(dwData);
        if ((RegQueryValueEx (hk, L"ScoSampleSize", NULL, &dwType, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS) &&
            (dwType == REG_DWORD) && (dwSize == sizeof(dwData)) && dwData)
            pParms->iScoSampleSize = dwData;

        RegCloseKey(hk);        
    }

exit:
    return fRetVal;
}


/*
 * This function is called from HCI to start the BT hardware.  In the case of SDIO
 * it just checks if we are attached.
 */
int HCI_StartHardware(void)
{
    int fRetVal = TRUE;


    DBGPRINT(DBG_HCI, (L"HCI_StartHardware\n\r"));

    /* Call Callback function to inform DEVICE_UP */
    if (!pFnHCICallback)
    {
        DBGPRINT(DBG_HCI, (L"HCI Callback Function is NULL !\n\r"));
        return FALSE;
    }

    fRetVal = (ERROR_SUCCESS == pFnHCICallback(DEVICE_UP, NULL));

    return fRetVal;    
}


/*
 * This function is called from HCI to stop the BT hardware.
 */
int HCI_StopHardware(void)
{
    int fRetVal = TRUE;

    DBGPRINT(DBG_HCI, (L"HCI_StopHardware\n\r"));

    /* Call Callback function to inform DEVICE_DOWN */
    if (!pFnHCICallback)
    {
        DBGPRINT(DBG_HCI, (L"HCI Callback Function is NULL !\n\r"));
        return FALSE;
    }

    fRetVal = (ERROR_SUCCESS == pFnHCICallback(DEVICE_DOWN, NULL));

    return fRetVal;    
}


/*
 * This function is called from HCI to set the callback for indicating transport events.
 */
int HCI_SetCallback(HCI_TransportCallback pfCallback)
{
    DBGPRINT(DBG_HCI, (L"HCI_SetCallback\n\r"));

    //
    // If pfCallback is NULL, the driver must prepare to be unloaded.
    // The function will only be called with pfCallback set to NULL 
    // if connection to the stack is currently closed.
    //
    // If pfCallback is not NULL, this is the callback that is used to 
    // indicate hardware insertion/removal.
    //
    if (pfCallback == NULL)
    {
        DBGPRINT(DBG_HCI, (L"pfCallback is NULL !\n\r"));
    }

    pFnHCICallback = pfCallback;

    return ERROR_SUCCESS;   
}


/*
 * This function is called from HCI to open the transport connection.
 * Once this function returns, the transport is ready to read/write data.
 */
int HCI_OpenConnection(void)
{
    int fRetVal = TRUE;

    DBGPRINT(DBG_HCI, (L"HCI_OpenConnection\n\r"));

    /* For LTK 3070 Bluetooth Test */
    Adapter->bIsOpenConnection = TRUE;

    return fRetVal;    
}


/*
 * This function is called from HCI to close the transport connection.
 */
void HCI_CloseConnection(void)
{
    DBGPRINT(DBG_HCI, (L"HCI_CloseConnection\n\r"));

    /* For LTK 3070 Bluetooth Test */
    Adapter->bIsOpenConnection = FALSE;
    SetEvent(Adapter->hReadPacketEvent);
}


/*
 * This function is called from HCI to read data from the BT card.
 * This function will block until data is read.
 */
int HCI_ReadPacket(HCI_TYPE *peType, BD_BUFFER *pBuff)
{
    int fRetVal = TRUE;
    unsigned int iRead = 0;
    UCHAR bType = 0;


    DBGPRINT(DBG_HCI, (L"HCI_ReadPacket\n\r"));

    if (!HandleHCIReadPacket(Adapter, &pBuff->pBuffer[pBuff->cStart], &iRead, &bType))
    {
        DBGPRINT(DBG_HCI, (L"HCI_ReadPacket - Error reading packet from SDIO device.\n\r"));
        fRetVal = FALSE;
        goto exit;
    }

    pBuff->cEnd = pBuff->cStart + iRead;
    pBuff->cStart = pBuff->cStart + SDIO_HEADER_LEN;

    *peType = (HCI_TYPE)bType;  // SDIO type maps to HCI_TYPE
    
    DBGPRINT(DBG_HCI, (L"Received packet type: %d\n\r", *peType));
    // Adding below debug hexdump will cause problem if Rx Q not used
    HexDump(DBG_HCI, "ReadPacket Buffer: ", &pBuff->pBuffer[pBuff->cStart], iRead-SDIO_HEADER_LEN);

    if ((*peType != DATA_PACKET_ACL) &&
        (*peType != DATA_PACKET_SCO) &&
        (*peType != EVENT_PACKET))
    {
        DBGPRINT(DBG_HCI, (L"HCI_ReadPacket - Received an unknown packet type.\n\r"));    
        fRetVal = FALSE;
        goto exit;
    }

exit:
    return fRetVal;
}


/*
 * This function is called from HCI to write data down to the BT card.
 */
int HCI_WritePacket(HCI_TYPE eType, BD_BUFFER *pBuff)
{
    int fRetVal = TRUE;
    unsigned long ulWriteLen = BufferTotal(pBuff);
    SD_TRANSPORT_HEADER header;
    MRVL_STATUS Status;


    DBGPRINT(DBG_HCI, (L"HCI_WritePacket\n\r"));
    DBGPRINT(DBG_HCI, (L"type 0x%02x, len %d\n\r", eType, ulWriteLen));
    HexDump(DBG_HCI, "WritePacket Buffer: ", pBuff->pBuffer + pBuff->cStart, ulWriteLen);

    // Size and Type are stored in four bytes before cStart
    // SDIO service ID maps directly to HCI_TYPE
    header.u.AsULONG = ulWriteLen + SDIO_HEADER_LEN;
    header.u.AsUCHAR.ServiceID = eType;

#ifdef BT_DEBUG
    if (eType == HCI_COMMAND_PKT)
        HCI_CommandInfo(pBuff->pBuffer + pBuff->cStart);
#endif	

    memmove(pBuff->pBuffer, &header, sizeof(header));

    Status = SendSinglePacket(pBuff->pBuffer, ulWriteLen + SDIO_HEADER_LEN);
    if (Status != MRVL_STATUS_SUCCESS)
    {
        DBGPRINT(DBG_HCI, (L"Write Packet FAIL !\n\r"));
        fRetVal = FALSE;
    }
    else
    {
        DBGPRINT(DBG_HCI, (L"Write Packet OK !\n\r"));
        fRetVal = TRUE;
    }

    return fRetVal;
}
